use std::cell::RefCell;
use std::sync::Mutex;

use bytes::{BufMut, BytesMut};
use nats::Connection;
use once_cell::sync::Lazy;

use crate::codec;
use crate::message::HoneybeeMessage;
use crate::Result;

static TEMP: Lazy<Mutex<Option<Connection>>> = Lazy::new(Mutex::default);

static CONNECTION: Lazy<Connection> = Lazy::new(|| TEMP.lock().unwrap().take().unwrap());

thread_local! {
    static BUFFER: RefCell<BytesMut> = RefCell::new(BytesMut::with_capacity(32 * 1204));
}

pub fn connect(nats_url: &str) -> Result<()> {
    let nc = nats::connect(nats_url)?;
    *TEMP.lock().unwrap() = Some(nc);
    Ok(())
}

pub fn connection() -> &'static Connection {
    &CONNECTION
}

pub fn publish<T>(subject: &str, reply: &str, msg: T)
where
    T: HoneybeeMessage,
{
    BUFFER.with(move |buffer| {
        let mut buffer = buffer.borrow_mut();
        let serialized_size = match bincode::serialized_size(&msg) {
            Ok(size) => size,
            Err(err) => {
                error!(
                    "bincode serialize_into subject:{} replay:{} failed:{}",
                    subject, reply, err
                );
                return;
            }
        };
        let length = codec::U16_LENGTH + serialized_size as usize;
        buffer.clear();
        buffer.reserve(length);
        buffer.put_u16(T::ID);
        if let Err(err) = bincode::serialize_into((&mut (*buffer)).writer(), &msg) {
            error!(
                "bincode serialize_into subject:{} replay:{} failed:{}",
                subject, reply, err
            );
            return;
        }
        if let Err(err) = connection().publish_request(subject, reply, buffer.as_ref()) {
            error!(
                "publish_request subject:{} replay:{} failed:{}",
                subject, reply, err
            );
            return;
        }
    });
}
